package com.yuneec.droneservice.tools;

import com.yuneec.droneservice.ServiceManager;
import com.yuneec.droneservice.command.MAV_RC_ERROR_CODE;
import com.yuneec.droneservice.request.IRequest;
import com.yuneec.droneservice.request.IResponse;
import com.yuneec.droneservice.request.IResponseHandler;
import com.yuneec.droneservice.request.OnRequestListener;

import java.util.LinkedList;

public class SerialCommand {
    private LinkedList<IRequest> reqs;
    private LinkedList<OnRequestListener> cbs;
    private OnRequestListener<IResponse<Void>> onFinished;
    private boolean force = true;

    private SerialCommand() {
        reqs = new LinkedList<>();
        cbs = new LinkedList<>();
    }

    public SerialCommand force(boolean force) {
        this.force = force;
        return this;
    }

    public SerialCommand onFinished(OnRequestListener<IResponse<Void>> onFinished) {
        this.onFinished = onFinished;
        return this;
    }

    public static class INextRequest extends IRequest {
        private IRequest iRequest;

        public INextRequest() {
            super(-1);
        }

        public void setRequest(IRequest req) {
            iRequest = req;
        }

        @Override
        public boolean hasReply() {
            return false;
        }

        @Override
        public boolean parseReply(IResponseHandler irh, OnRequestListener listener) {
            return false;
        }

        @Override
        public int port() {
            return -1;
        }

        @Override
        public byte[] toRawData() {
            return null;
        }
    }

    public SerialCommand next(IRequest req, OnRequestListener cb) {
        if (req == null) {
            return this;
        }
        reqs.add(req);
        cbs.add(cb);
        return this;
    }

    public void request() {
        IRequest req;
        try {
            req = reqs.removeFirst();
        } catch (Exception e) {
            req = null;
        }
        if (req == null) {
            RCLog.d("all command is over!");
            onFinished.onSuccess(new IResponse<Void>(-1, MAV_RC_ERROR_CODE.MAV_RC_ERR_SUCCESS));
            return;
        }
        OnRequestListener tmpCb;
        try {
            tmpCb = cbs.removeFirst();
        } catch (Exception e) {
            tmpCb = null;
        }
        final OnRequestListener cb = tmpCb;
        RCLog.d("do send command :" + req);
        if (req instanceof INextRequest) {
            req = INextRequest.class.cast(req).iRequest;
        }
        ServiceManager.getInstance().sendDroneCommand(req, new OnRequestListener() {
            @Override
            public void onSuccess(IResponse response) {
                RCLog.d("serial command onSuccess");
                boolean next = true;
                try {
                    if (cb != null) {
                        cb.onSuccess(response);
                    }
                } catch (Exception e) {
                    next = force | false;
                    RCLog.d("SerialCommand onSuccess", e);
                }
                RCLog.d("isNext:" + next);
                if (next) {
                    request();
                } else {
                    onFinished.onError(MAV_RC_ERROR_CODE.MAV_RC_ERR_UNKNOW);
                }
            }

            @Override
            public void onError(int errorCode) {
                RCLog.d("serial command onError:" + errorCode);
                try {
                    if (cb != null) {
                        cb.onError(errorCode);
                    }
                } catch (Exception e) {
                    RCLog.d("SerialCommand onError:" + errorCode, e);
                }
                if (force) {
                    request();
                } else {
                    onFinished.onError(errorCode);
                }
            }

            @Override
            public void onTimeout() {
                RCLog.d("serial command onTimeout");
                try {
                    if (cb != null) {
                        cb.onTimeout();
                    }
                } catch (Exception e) {
                    RCLog.d("SerialCommand onTimeout", e);
                }
                if (force) {
                    request();
                } else {
                    onFinished.onTimeout();
                }
            }
        });
    }

    public static SerialCommand newInstance() {
        return new SerialCommand();
    }

}
